﻿using System;
using System.Collections.Generic;
using SimpleWorkflow.Data;
using SimpleWorkflow.Patterns;

namespace SimpleWorkflow.Steps
{
    public delegate T GenericEvaluationDelegate<out T>();

    /// <summary>
    /// Make a generic decision and branch accordingly.
    /// There are two uses: 
    /// 1) subclass and override Evaluate
    /// 2) create with evaluate delegate to ctor
    /// </summary>
    public class GenericDecisionStep<T> : SimpleStep<T>, ILinearSteps
        where T : ISharedFlowContext
    {
        public GenericEvaluationDelegate<string> Evaluation;
        private Dictionary<string, ISimpleStep> Branches { get; set; }

        [ComplexStepExtension]
        public bool? ExceptionOnMissingBranch { get; set; }

        protected GenericDecisionStep(GenericEvaluationDelegate<string> evaluation)
            : this()
        {
            Evaluation = evaluation;
        }

        protected GenericDecisionStep()
        {
            Branches = new Dictionary<string, ISimpleStep>();
        }

        public override void Run()
        {
            var result = Evaluate();
            ISimpleStep branch;

            if (Branches.TryGetValue(result, out branch))
                RunProxy(branch);
            else if (ExceptionOnMissingBranch ?? false)
                throw new Exception("Branch not found for result: " + result);
        }

        public void AddBranch(string branchMatchValue, LinearSteps<T> branch)
        {
            if (string.IsNullOrEmpty(branchMatchValue))
                throw new ApplicationException("Adding branch but branch match value is null.  Suspected LinearSteps was constructed from ContainerSegment that did not specify a 'ReferenceKey' ComplexStepExtensionValue.");
            Branches[branchMatchValue] = branch;
        }

        public void AddStep(ISimpleStep step)
        {
            if (!(step is LinearSteps<T>))
                throw new Exception("Branch being added is not of LinearSteps");

            var linearStep = (LinearSteps<T>) step;
            AddBranch(linearStep.ReferenceKey, linearStep); 
        }

        public virtual string Evaluate()
        {
            if (Evaluation == null)
                throw new Exception("Decision step ran with no evaluation delegate set or subclasses evaluate method not overridden.");
            return Evaluation();
        }
    }
}
